package com.danbay.cloud.device.service;

import com.danbay.cloud.base.constants.Const;
import com.danbay.cloud.base.constants.DeviceType;
import com.danbay.cloud.device.api.dto.CollectorCountDto;
import com.danbay.cloud.device.api.dto.ReportDataDto;
import com.danbay.cloud.device.api.dto.ReportRelayDto;
import com.danbay.cloud.device.api.dto.WiredMeterDataReportDto;
import com.danbay.cloud.device.api.service.IDeviceReportDataService;
import com.danbay.cloud.device.config.DeviceConfig;
import com.danbay.cloud.device.constant.DeviceCommonConstant;
import com.danbay.cloud.device.entity.DeviceCollector;
import com.danbay.cloud.device.entity.EnergyDayConsumption;
import com.danbay.cloud.device.entity.EnergyDevice;
import com.danbay.cloud.device.entity.EnergyEmWmConsumption;
import com.danbay.cloud.device.mapper.DeviceCollectorMapper;
import com.danbay.cloud.device.mapper.EnergyDayConsumptionMapper;
import com.danbay.cloud.device.mapper.EnergyDeviceMapper;
import com.danbay.cloud.device.mapper.EnergyEmWmConsumptionMapper;
import com.danbay.framework.exception.BusinessException;
import com.danbay.framework.utils.BeanValidator;
import com.danbay.framework.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * 设备数据上报服务
 *
 * @author SJG
 * @date 2017/10/23
 */
@Service
@Slf4j
public class DeviceReportDataService implements IDeviceReportDataService {

    @Autowired
    private EnergyDeviceMapper energyDeviceMapper;

    @Autowired
    private EnergyEmWmConsumptionMapper energyEmWmConsumptionMapper;

    @Autowired
    private EnergyDayConsumptionMapper energyDayConsumptionMapper;

    @Autowired
    private DeviceConfig deviceConfig;

    @Autowired
    private DeviceCollectorMapper deviceCollectorMapper;

    /**
     * 上报实抄数据
     *
     * @param reportDataDto 上报读数
     * @throws BusinessException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void realData(ReportDataDto reportDataDto) throws BusinessException {
        updateDeviceConsumption(reportDataDto);
    }

    /**
     * 上报日冻结数据
     *
     * @param reportDataDto 上报读数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void freezeData(ReportDataDto reportDataDto) throws BusinessException {
        updateDeviceConsumption(reportDataDto);
    }

    /**
     * 设备主动数据上报
     *
     * @param reportDataDto 上报读数
     * @throws BusinessException
     */
    @Override
    public void dataReport(@RequestBody ReportDataDto reportDataDto) throws BusinessException {
        updateDeviceConsumption(reportDataDto);
    }

    @Override
    public void reportRelay(ReportRelayDto reportRelayDto) throws BusinessException {
        //根据设备ID更改电表的拉合闸状态
        EnergyDevice energyDevice = energyDeviceMapper.selectOne(EnergyDevice.builder()
                .deviceId(reportRelayDto.getDeviceId()).build());
        if (null == energyDevice){
            throw new BusinessException("查询不到设备,设备ID为"+reportRelayDto.getDeviceId());
        }
        if (reportRelayDto.isValue()){
            energyDevice.setSwitch_state(DeviceCommonConstant.SWITCH_OPEN);
        }else {
            energyDevice.setSwitch_state(DeviceCommonConstant.SWITCH_CLOSE);
        }
        energyDeviceMapper.updateById(energyDevice);
    }

    @Override
    public void wiredDataReport(WiredMeterDataReportDto wiredMeterDataReport) throws BusinessException {
        dealWiredMeterDataReport(wiredMeterDataReport);
    }

    @Override
    public void getCount(CollectorCountDto collectorCountDto) throws BusinessException {
        log.info("采集器:{},下接档案数:{}",collectorCountDto.getDeviceId(),collectorCountDto.getCount());
        DeviceCollector deviceCollector = deviceCollectorMapper.selectOne(DeviceCollector.builder().deviceId(collectorCountDto.getDeviceId()).build());
        if (null == deviceCollector){
            throw new BusinessException("设备不存在"+collectorCountDto.getDeviceId());
        }
        deviceCollector.setMeterNum(collectorCountDto.getCount());
        deviceCollectorMapper.updateById(deviceCollector);
    }

    private void dealWiredMeterDataReport(WiredMeterDataReportDto wiredMeterDataReport) throws BusinessException{
        EnergyDevice energyDevice = energyDeviceMapper.selectOne(EnergyDevice.builder().address(wiredMeterDataReport.getNumber()).build());
        if (null == energyDevice){
            //数据不存在
            throw new BusinessException("查询不到设备,设备表号为"+wiredMeterDataReport.getNumber());
        }
        if (wiredMeterDataReport.getMeterCount() < 0 ){
            log.warn("设备表号:{}上报数据小于0.",wiredMeterDataReport.getNumber());
            return;
        }
        //转换电表读数
        Double readValue = wiredMeterDataReport.getMeterCount() * deviceConfig.getPrecision().getKwhPercent();
        //处理读数异常数据
        if (readValue > Const.MAX_ALLOW_KWH) {
            log.error("设备表号:{}上报异常数据",wiredMeterDataReport.getNumber());
            return;
        }
        dealMeterReport(energyDevice,wiredMeterDataReport.getReadDate(),readValue);
    }

    /**
     * 更新设备（水电表）能耗
     *
     * @param reportDataDto 上报读数
     */
    private void updateDeviceConsumption(ReportDataDto reportDataDto) throws BusinessException {
        //验证参数
        BeanValidator.valid(reportDataDto);

        //获取能耗设备
        EnergyDevice energyDevice = energyDeviceMapper.selectOne(EnergyDevice.builder().deviceId(reportDataDto.getDevId()).build());
        if (energyDevice == null) {
            throw new BusinessException("设备不存在devId:" + reportDataDto.getDevId());
        }
        if (reportDataDto.getMeterCount() < 0) {
            log.warn("上报读数:{}小于0,不处理", reportDataDto.getMeterCount());
            return;
        }

        //添加上报记录
        EnergyEmWmConsumption energyEmWm = new EnergyEmWmConsumption();
        energyEmWm.setUpTime(DateUtils.parse(reportDataDto.getDate(), DateUtils.YMDHMS2).toDate());
        energyEmWm.setKwh(0d);
        energyEmWm.setFlow(0d);
        energyEmWm.setEnergyDevice(energyDevice.getId());
        //读数单位转换
        //设备上传的整数读数转为真实小数读数时需保留的精度小数位
        //如上传1100,配置精度为2,转为真实小数读数=>1100/100=11,即1100*0.01=11
        Double readValue;
        DeviceType deviceType = DeviceType.parse(energyDevice.getName());
        if (deviceType == DeviceType.EM_METER) {
            //电量单位转换
            readValue = reportDataDto.getMeterCount() * deviceConfig.getPrecision().getKwhPercent();
            //异常情况可能会出现大于10w
            if (readValue > Const.MAX_ALLOW_KWH) {
                log.error("meter count gt 10W");
                return;
            }
            energyEmWm.setKwh(readValue);
        } else {
            //水量单位转换
            readValue = reportDataDto.getMeterCount() * deviceConfig.getPrecision().getWaterPercent();
            energyEmWm.setFlow(readValue);
        }

        if (energyDevice.getMeterCount() != null && reportDataDto.getMeterCount() < energyDevice.getMeterCount()) {
            log.warn("上报读数:{}小于原读数:{},不处理", reportDataDto.getMeterCount(), energyDevice.getMeterCount());
            return;
        }

        //插入上报记录
        energyEmWmConsumptionMapper.insert(energyEmWm);

        dealMeterReport(energyDevice,reportDataDto.getDate(),readValue);

    }

    private void dealMeterReport(EnergyDevice energyDevice,String readTime,double readValue){
        //上报读数时间
        DateTime readDate = DateUtils.parse(readTime, DateUtils.YMDHMS2);

        //保存日冻结数据到EnergyDayConsumption表
        EnergyDayConsumption energyDayConsumption = energyDayConsumptionMapper.selectOne(
                //取上报当天的日冻结记录
                EnergyDayConsumption.builder()
                        .year(readDate.getYear())
                        .month(readDate.getMonthOfYear())
                        .day(readDate.getDayOfMonth())
                        .energyDevice(energyDevice.getId())
                        .build()
        );
        //新增
        if (energyDayConsumption == null) {
            energyDayConsumption = new EnergyDayConsumption();
            energyDayConsumption.setMeterCount(0d);
            energyDayConsumption.setYear(readDate.getYear());
            energyDayConsumption.setMonth(readDate.getMonthOfYear());
            energyDayConsumption.setDay(readDate.getDayOfMonth());
            energyDayConsumption.setAddTime(DateTime.now().toDate());
        }

        //如果读数小于之前已上报的读数，则不处理；
        if (readValue < energyDayConsumption.getMeterCount()) {
            log.info("读数:{}小于之前已上报的读数:{},不处理", readValue, energyDayConsumption.getMeterCount());
            return;
        }
        energyDayConsumption.setEnergyDevice(energyDevice.getId());
        //读数时间
        energyDayConsumption.setReadTime(readDate.toDate());
        //冻结时间，日冻结数据上报时设置，此处使用数据上报时间
        energyDayConsumption.setFrozenTime(readDate.toDate());

        energyDayConsumption.setMeterCount(readValue);
        //todo 暂无用
        energyDayConsumption.setDataSource(3);
        //todo 暂无用
        energyDayConsumption.setCost(0d);

        if (energyDayConsumption.getId() == null) {
            energyDayConsumptionMapper.insert(energyDayConsumption);
        } else {
            energyDayConsumptionMapper.updateById(energyDayConsumption);
        }

        //得到本月用电数= 本月最后一条记录数 - 上月最后一条记录数
        //先取上月最后一条记录；
        Double preMonthLast = 0d;
        DateTime preMonth = readDate.withDayOfMonth(1).minusDays(1);
        EnergyDayConsumption lastRecord = energyDayConsumptionMapper.getMonthLastRecord(
                EnergyDayConsumption.builder()
                        .energyDevice(energyDevice.getId())
                        .year(preMonth.getYear())
                        .month(preMonth.getMonthOfYear())
                        .build()
        );
        //如果上月记录则用，否则为0
        if (lastRecord != null) {
            preMonthLast = lastRecord.getMeterCount();
        }

        //再取本月最后一条记录，即当前上报读数
        Double currMonthLast = readValue;
        //本月用电数= 本月最后一条读数 - 上月最后一条读数
        energyDevice.setMeterMonthCount(currMonthLast - preMonthLast);

        //取昨天读数
        DateTime yesterday = readDate.minusDays(1);
        EnergyDayConsumption yesterdayRecord = energyDayConsumptionMapper.selectOne(
                EnergyDayConsumption.builder()
                        .energyDevice(energyDevice.getId())
                        .year(yesterday.getYear())
                        .month(yesterday.getMonthOfYear())
                        .day(yesterday.getDayOfMonth())
                        .build()
        );

        //如果昨天有读数则更新昨天读数
        if (yesterdayRecord != null) {
            energyDevice.setMeterYesterdayCount(yesterdayRecord.getMeterCount());
        }
        energyDevice.setMeterCount(readValue);
        //更新设备读数和时间
        //不做单位转换的原始读数
        energyDevice.setReal_meter_count(String.valueOf(readValue));
        energyDevice.setReadTime(readDate.toDate());
        energyDevice.setReal_meter_count_time(DateUtils.strTranslate(readTime, DateUtils.YMDHMS2, DateUtils.YMDHMS1));

        //保存修改
        energyDeviceMapper.updateById(energyDevice);
    }


}
